/**
 * 
 */
package org.vsg.stock.basic.service.business;

import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.vsg.common.biz.exception.ServiceException;
import org.vsg.stock.basic.domain.BalanceLine;
import org.vsg.stock.basic.domain.BalanceLineKeyPoint;
import org.vsg.stock.basic.persistence.BalanceLineDao;
import org.vsg.stock.basic.persistence.BalanceLineKeypointDao;
import org.vsg.stock.basic.service.StockBusinessHelper;

/**
 * @author Bill Vison
 *
 */
public abstract class AbstractStockBalanceLineFinderBO {
	
	private String stockCode;
	
	@Resource
	private BalanceLineDao balanceLineDao;
	
	@Resource
	private BalanceLineKeypointDao  balanceLineKeypointDao;
	
	static protected DateFormat df = new SimpleDateFormat("yyyy-MM-dd");		

	public String getStockCode() {
		return stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}
	/**
	 * scan find all lines in cycle refered to from recent date
	 * @param recentDate
	 * @param aheadCycle
	 * @return
	 * @throws ServiceException
	 */
	public abstract Collection<BalanceLine> scanLines(Date recentDate , int aheadCycle) throws ServiceException;
	
	/**
	 * 
	 * @param importNewLines
	 * @return
	 * @throws ServiceException
	 */
	public Collection<BalanceLine> filterAndMergeForNewLines(Collection<BalanceLine> importNewLines) throws ServiceException {

		// --- merge ---
		Map<BalanceLineKeyPoint, Set<String>>  pointMapToLineIds =  balanceLineDao.findAndByOrgBalaline((List<BalanceLine>)importNewLines);

		Collection<BalanceLine> balaLineList = new Vector<BalanceLine>();
		
		// --- find by point ---
		for (BalanceLine line : importNewLines) {
			
			Map<String, Integer> assoLinesCounter = new HashMap<String, Integer>();
			
			for (BalanceLineKeyPoint point : line.getKeyPoints() ) {

				// --- find line id by point ----
				Set<String>  assoExistLineIds = pointMapToLineIds.get(point);
				// --- count asso line link ----
				
				for (String lineId : assoExistLineIds) {
					int counter = 0;
					if ( assoLinesCounter.get(lineId) != null ) {
						counter = assoLinesCounter.get(lineId);
					}
					counter += 1;
					assoLinesCounter.put(lineId, counter);
				}
			}
			
			// ---- find the closely match ----
			String[]  lineIds = findMostCloselyLineIds(assoLinesCounter);
			
			// --- add the new handle ---
			if (lineIds.length == 0) {
				
				// --- build proxy ---
				org.vsg.stock.basic.persistence.mybatis.proxy.BalanceLineProxy 
					proxy = new org.vsg.stock.basic.persistence.mybatis.proxy.BalanceLineProxy();
				proxy.setExisted( false );
				
				try {
					
					BeanUtils.copyProperties(proxy, line);
					balaLineList.add( proxy );					
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}	
			
			}
			else {
				// --- re assign for the old line ---
				List<BalanceLineKeyPoint> assignNewPoints = line.getKeyPoints();
				
				BalanceLineKeyPoint newLineKeyPoint = findKeyPointInMiddleList( assignNewPoints );
				
				for (String lineId : lineIds) {
					
					double averagePrice = balanceLineKeypointDao.getAveragePriceByLineId(lineId);
					
					// -- fine the close price ---
					BalanceLineKeyPoint nearestPoint = balanceLineKeypointDao.getDifferenceFromCloselyPriceByLineId(lineId, newLineKeyPoint.getPrice() - averagePrice );
					
					// --- match and assing to old line
					List<BalanceLineKeyPoint> assoSameLinePoints = assinedToOldLine(nearestPoint , assignNewPoints);
					
					org.vsg.stock.basic.persistence.mybatis.proxy.BalanceLineProxy 
					proxy = new org.vsg.stock.basic.persistence.mybatis.proxy.BalanceLineProxy();
					proxy.setExisted( true );
					
					try {
						
						BeanUtils.copyProperties(proxy, line);
						// --- add new point and line ----
						proxy.setLineId(lineId);
						proxy.setKeyPoints( assoSameLinePoints );
						
						balaLineList.add( proxy );
						
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}					
				}
				
				
				// ---- create the new line  for remain points ---
				if ( assignNewPoints.size() > 2) {
					// ---- create a new line ---
					org.vsg.stock.basic.persistence.mybatis.proxy.BalanceLineProxy 
					proxy = new org.vsg.stock.basic.persistence.mybatis.proxy.BalanceLineProxy();
					proxy.setExisted( false );
					
					try {
						
						BeanUtils.copyProperties(proxy, line);
						
						balaLineList.add( proxy );
						
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}					
					
					
				}
				
				
				
				
			}			
		}
		
		
		// --- find key point map any to line ---
		Set<BalanceLineKeyPoint> basicKeyPointSet = findExistedPoint(pointMapToLineIds);
		
		// ---- remove point and lines ---
		removePointsAndLines(balaLineList , basicKeyPointSet);
		
		
		return balaLineList;
	}
	
	
	/**
	 * for new line
	 * @param mapPoint
	 * @return
	 */
	private Set<BalanceLineKeyPoint> findExistedPoint(Map<BalanceLineKeyPoint, Set<String>> mapPoint) {
		
		Set<BalanceLineKeyPoint> keyPointSet = new HashSet<BalanceLineKeyPoint>();
		
		Iterator<Map.Entry<BalanceLineKeyPoint, Set<String>>> entryIter = mapPoint.entrySet().iterator();
		while ( entryIter.hasNext() ) {
			Map.Entry<BalanceLineKeyPoint, Set<String>> entry = entryIter.next();
			if (entry.getValue().size() != 0) {
				keyPointSet.add( entry.getKey() );
			}
			
		}
		return keyPointSet;
	}
	
	
	private List<BalanceLineKeyPoint> assinedToOldLine(BalanceLineKeyPoint nearestPoint , List<BalanceLineKeyPoint> inputPointList) {
		
		double standarPrice = nearestPoint.getPrice();
		
		double max = StockBusinessHelper.getDiffRelativedPrice(standarPrice, StockBusinessHelper.MAX_DIFF);
		double min = StockBusinessHelper.getDiffRelativedPrice(standarPrice, StockBusinessHelper.MIN_DIFF);		
		
		BalanceLineKeyPoint refPoint = null;
		
		List<BalanceLineKeyPoint> listPoints = new Vector<BalanceLineKeyPoint>();
		
		for (Iterator<BalanceLineKeyPoint> inputPointIter = inputPointList.iterator() ; inputPointIter.hasNext(); ) {
			refPoint = inputPointIter.next();
			
			if (refPoint.getPrice() <= max && refPoint.getPrice() >= min) {
				listPoints.add(refPoint);
				inputPointIter.remove();
			}

		}
		
		return listPoints;
	}
	
	
	/**
	 * 
	 * @param keyPointList
	 * @return
	 */
	private BalanceLineKeyPoint findKeyPointInMiddleList(List<BalanceLineKeyPoint> keyPointList) {

		// --- sort by array ---
		Collections.sort(keyPointList , new Comparator<BalanceLineKeyPoint>() {
			@Override
			public int compare(BalanceLineKeyPoint o1,	BalanceLineKeyPoint o2) {
				if (o1.getPrice() > o2.getPrice() ) {
					return 1;
				} else if (o1.getPrice() < o2.getPrice()) {
					return -1;
				}

				return 0;

			}
		});
		
		int index = keyPointList.size() / 2;
		//int mod = keyPointList.size() % 2;
		
		if (index == 0) {
			if ( keyPointList.size() == 0) {
				return null;
			} else {
				return keyPointList.get(0);
			}
		}
		
		return keyPointList.get(index);
	}
	
	private void removePointsAndLines(Collection<BalanceLine> balaLineList , Set<BalanceLineKeyPoint>  pointExisted ) {
	
		for (Iterator<BalanceLine> lineIter = balaLineList.iterator() ; lineIter.hasNext(); ) {
			
			BalanceLine line = lineIter.next();
			
			for (Iterator<BalanceLineKeyPoint>  keyPointsIter = line.getKeyPoints().iterator() ; keyPointsIter.hasNext() ; ) {
				if ( pointExisted.contains(  keyPointsIter.next()) ) {
					keyPointsIter.remove();
				}
			}
			// clear the line if now key point associate 
			if ( line.getKeyPoints().size() == 0) {
				lineIter.remove();
			}
			
		}

	}
	
	
	/**
	 * find the line refer to haft of points . if has a line , assign to this line , 
	 * if not a line. create new line or join a new line 
	 * @param assoLinesCounterMap
	 * @return
	 */
	private String[] findMostCloselyLineIds(Map<String, Integer> assoLinesCounterMap) {
		
		Collection<String> assoLineIds = new Vector<String>();
		
		for (Map.Entry<String, Integer> entry : assoLinesCounterMap.entrySet()) {
			assoLineIds.add( entry.getKey() );
		}


		return assoLineIds.toArray(new String[0]);
	}
	/*
	private static Map<String, Integer> sortByValue(Map<String, Integer> map) {
		List<Map.Entry<String, Integer>> list = new LinkedList<Map.Entry<String, Integer>>(map.entrySet());
		Collections.sort(list , new Comparator<Map.Entry<String, Integer>>() {
			@Override
			public int compare(Entry<String, Integer> o1,
					Entry<String, Integer> o2) {
				
				if (o1.getValue() > o2.getValue()) {
					return -1;
				} else if (o1.getValue() < o2.getValue()) {
					return 1;
				}

				return 0;
			}
		});
		
		Map<String, Integer> result = new LinkedHashMap<String, Integer>();
		for (Iterator<Map.Entry<String, Integer>> it = list.iterator() ; it.hasNext();) {
			Map.Entry<String, Integer> entry = (Map.Entry<String, Integer>) it.next();
			result .put(entry.getKey(), entry.getValue());
		}
		return result;
	}
	*/
	

	/**
	 * 
	 * @return
	 */
	public abstract String getFinderCode();
	

	
	
	

}
